package com.ai.service.made.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ai.check.SKExpressionRoot;
import com.ai.common.CommonResponse;
import com.ai.common.constant.*;
import com.ai.entity.BasePage;
import com.ai.entity.made.ModbusServerInfo;
import com.ai.entity.made.OpcNodeInfo;
import com.ai.entity.made.OpcServerInfo;
import com.ai.entity.made.RegionBase;
import com.ai.entity.made.page.OpcServerInfoPage;
import com.ai.mapper.made.OpcServerInfoMapper;
import com.ai.model.dto.OpcServerInfoDto;
import com.ai.model.vo.DeviceInfoVo;
import com.ai.model.vo.OpcServerInfoVo;
import com.ai.model.vo.SysRoleVo;
import com.ai.opc.DynamicOpcNodeSubscriber;
import com.ai.opc.OpcThreadPoolExecutor;
import com.ai.service.made.IModbusServerInfoService;
import com.ai.service.made.IOpcNodeInfoService;
import com.ai.service.made.IOpcServerInfoService;
import com.ai.service.made.IRegionBaseService;
import com.ai.util.SecurityUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.api.UaClient;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jiaok
 * @since 2024-11-14
 */
@Service
@Slf4j
public class OpcServerInfoServiceImpl extends ServiceImpl<OpcServerInfoMapper, OpcServerInfo> implements IOpcServerInfoService {

    @Resource
    private IRegionBaseService iRegionBaseService;

    @Autowired
    private IOpcNodeInfoService iOpcNodeInfoService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Resource
    private SKExpressionRoot skExpressionRoot;


    @Resource
    private IModbusServerInfoService iModbusServerInfoService;

    @Override
    public CommonResponse<String> add(OpcServerInfoDto opcServerInfoDto) {

        OpcServerInfo opcServerInfo = new OpcServerInfo();
        BeanUtils.copyProperties(opcServerInfoDto,opcServerInfo);
        opcServerInfo.setCommunicationNodeNum(0);
        // TODO: 2024-11-14 状态通过枚举值设置 
        opcServerInfo.setState("0");
        if (StrUtil.isNotEmpty(opcServerInfoDto.getRegionBaseId())){
            opcServerInfo.setRegionBaseId(opcServerInfoDto.getRegionBaseId());
        }else {
            opcServerInfo.setRegionBaseId(SecurityUtils.getLoginRegionBaseId());
        }
        opcServerInfo.setCreateUser(SecurityUtils.getLoginUserName());
        opcServerInfo.setCreateTime(LocalDateTime.now());
        opcServerInfo.setUpdateUser(SecurityUtils.getLoginUserName());
        opcServerInfo.setUpdateTime(LocalDateTime.now());

        this.save(opcServerInfo);

        // TODO: 2024-11-14 开辟子线程，提交任务，连接opcServer
        OpcThreadPoolExecutor.startConnectOpcServerTask(opcServerInfo,this);

        return CommonResponse.ok("节点新建成功");
    }

    @Override
    public void updateOpcServerState(String opcServerId, String state) {

        this.update(new UpdateWrapper<OpcServerInfo>().eq("id",opcServerId).set("state",state));

    }

    @Override
    public void initConnectOpc() {
        //获取所有的节点信息

        List<OpcServerInfo> opcServerInfos = this.list();
        //开始建立连接监听所有的节点
        opcServerInfos.forEach(item ->{
            //获取该opc下的所有节点信息
            List<OpcNodeInfo> opcNodeInfoList = iOpcNodeInfoService.list(new QueryWrapper<OpcNodeInfo>().eq("opc_server_info_id", item.getId()));
            if (!CollectionUtils.isEmpty(opcNodeInfoList)){
                //缓存所有的node节点
                for (OpcNodeInfo opcNodeInfo : opcNodeInfoList) {
                    stringRedisTemplate.opsForValue().set(RedisConstants.OPC_NODE_NODE_INFO+opcNodeInfo.getIdentifier(),opcNodeInfo.getId());
                }
                OpcThreadPoolExecutor.initOpcConnectSubNode(item,this,opcNodeInfoList,iOpcNodeInfoService);
            }
        });
    }

    @Override
    public CommonResponse getOpcServerNameList() {

//        List<OpcServerInfo> serverInfoList = list(new QueryWrapper<OpcServerInfo>().select("id","opc_server_name").orderByDesc("create_time"));
//        if (CollectionUtils.isEmpty(serverInfoList)){
//            return CommonResponse.ok();
//        }else {
//            List<OpcServerInfo> sortedOpcServerInfoList = serverInfoList.stream().sorted(Comparator.comparing(OpcServerInfo::getOpcServerName)).collect(Collectors.toList());
//            return CommonResponse.ok(sortedOpcServerInfoList);
//        }

        List<DeviceInfoVo> deviceInfoVoList = new ArrayList<>();
        //查看当前是否为板集modbus管理
        if (skExpressionRoot.hasPermission(OperationPermissionConstant.BJ_MODBUS_DEVICE)){

            List<ModbusServerInfo> modbusServerInfoList = iModbusServerInfoService.list();
            if (!CollectionUtils.isEmpty(modbusServerInfoList)){
                deviceInfoVoList = modbusServerInfoList.stream().map(modbusServer -> {
                            DeviceInfoVo deviceInfoVo = new DeviceInfoVo();
                            deviceInfoVo.setDeviceServerName(modbusServer.getDeviceName());
                            deviceInfoVo.setDeviceServerId(modbusServer.getId());
                            deviceInfoVo.setDeviceType(ModbusConstants.DEVICE_TYPE);
                            return deviceInfoVo;
                        }).sorted(Comparator.comparing(DeviceInfoVo::getDeviceServerName))
                        .collect(Collectors.toList());
            }
        }
        //查看是否具有opc设备管理
        if (skExpressionRoot.hasPermission(OperationPermissionConstant.OPC_DEVICE)){
            List<String> belowRegionIdList = iRegionBaseService.getBelowRegionIdList(SecurityUtils.getLoginRegionBaseId());
            List<OpcServerInfo> serverInfoList = list(new QueryWrapper<OpcServerInfo>()
                    .select("id","opc_server_name")
                    .in("region_base_id",belowRegionIdList).orderByDesc("create_time"));
            if (!CollectionUtils.isEmpty(serverInfoList)){
                List<DeviceInfoVo> opcDeviceList = serverInfoList.stream().map(opcServerInfo -> {
                            DeviceInfoVo deviceInfoVo = new DeviceInfoVo();
                            deviceInfoVo.setDeviceServerName(opcServerInfo.getOpcServerName());
                            deviceInfoVo.setDeviceServerId(opcServerInfo.getId());
                            deviceInfoVo.setDeviceType(OpcConstant.DEVICE_TYPE);
                            return deviceInfoVo;
                        }).sorted(Comparator.comparing(DeviceInfoVo::getDeviceServerName))
                        .collect(Collectors.toList());
                deviceInfoVoList.addAll(opcDeviceList);
            }
        }

        DeviceInfoVo deviceInfoVo = new DeviceInfoVo();
        deviceInfoVo.setDeviceServerId(Constants.DEVICE_TYPE_SIGNAL_CAMERA_DEFAULT_ID);
        deviceInfoVo.setDeviceServerName("信号摄像头");
        deviceInfoVo.setDeviceType(Constants.DEVICE_TYPE_SIGNAL_CAMERA);
        deviceInfoVoList.add(deviceInfoVo);
        return CommonResponse.ok(deviceInfoVoList);
    }

    @Override
    public CommonResponse<String> delete(String id) {

        //检测当前opcServer下是否存有节点
        List<OpcNodeInfo> opcNodeInfoList = iOpcNodeInfoService.list
                (new QueryWrapper<OpcNodeInfo>().eq("opc_server_info_id", id));
        
        if (!CollectionUtils.isEmpty(opcNodeInfoList)){
            throw new RuntimeException("该设备下存有正在运行的节点，请先删除对应的节点");
        }
        
        //准备删除设备
        
        //断开opc连接
        disconnectOpc(id);
        //删除数据库
        this.removeById(id);
        
        return CommonResponse.ok("删除成功");
    }



    @Override
    public CommonResponse<String> edit(OpcServerInfoDto opcServerInfoDto) {

        if (StrUtil.isEmpty(opcServerInfoDto.getRegionBaseId())){
            throw new RuntimeException("所属组织id不能为空");
        }

        if (opcServerInfoDto.isNeedReconnect()){

            UaClient uaClient = OpcConstant.OPC_SERVER_ID_CLIENT_MAP.get(opcServerInfoDto.getId());
            if (!ObjectUtil.isEmpty(uaClient)){
                //断开所有节点的监控
                try {
//                    UaSubscription uaSubscription = OpcConstant.OPC_CLIENT_SUBSCRIBE_MAP.get(opcServerInfoDto.getId());
                    new DynamicOpcNodeSubscriber(uaClient,iOpcNodeInfoService).stop();
                } catch (Exception e) {
                    log.error("断开节点监控出现异常",e);
                }
                //断开设备连接
                disconnectOpc(opcServerInfoDto.getId());
            }

            //开始修改设备的信息
            OpcServerInfo opcServerInfo = getById(opcServerInfoDto.getId());
            BeanUtils.copyProperties(opcServerInfoDto,opcServerInfo);
            opcServerInfo.setUpdateTime(LocalDateTime.now());
            opcServerInfo.setUpdateUser(SecurityUtils.getLoginUserName());
            this.updateById(opcServerInfo);

            //开始连接opc设备
            OpcThreadPoolExecutor.startConnectOpcServerTask(opcServerInfo,this);
            //开始连接opc节点
            iOpcNodeInfoService.nodeConnectOpcServer(opcServerInfoDto.getId());

        }else {
            //只修改了opc设备的名称
            this.update(new UpdateWrapper<OpcServerInfo>().eq("id",opcServerInfoDto.getId()).set("opc_server_name",opcServerInfoDto.getOpcServerName()).set("region_base_id",opcServerInfoDto.getRegionBaseId()));
        }

        return CommonResponse.ok("修改成功");
        
    }

    @Override
    public IPage selectByPage(OpcServerInfoPage page) {
        IPage<OpcServerInfoVo> voIPage = new BasePage<>();
        List<String> belowRegionIdList = iRegionBaseService.getBelowRegionIdList(SecurityUtils.getLoginRegionBaseId());
        page.setRegionBaseIdList(belowRegionIdList);

        QueryWrapper<OpcServerInfo> queryWrapper = page.getQuery(new QueryWrapper<OpcServerInfo>());
        OpcServerInfoPage opcServerInfoPage = this.page(page, queryWrapper);
        List<OpcServerInfo> records = opcServerInfoPage.getRecords();
        List<OpcServerInfoVo> opcServerInfoVoList = records.stream().map(opcServerInfo -> {
            OpcServerInfoVo opcServerInfoVo = new OpcServerInfoVo();
            BeanUtils.copyProperties(opcServerInfo, opcServerInfoVo);
            RegionBase regionBase = iRegionBaseService.getById(opcServerInfo.getRegionBaseId());
            opcServerInfoVo.setRegionName(regionBase.getRegionName());
            return opcServerInfoVo;
        }).collect(Collectors.toList());

        BeanUtils.copyProperties(opcServerInfoPage,voIPage);
        voIPage.setRecords(opcServerInfoVoList);

        return voIPage;
    }

    @Override
    public void reconnectOpcServer(String opcServerId) {
        //获取指定的opc，
        OpcServerInfo opcServerInfo = getById(opcServerId);
        if (ObjectUtil.isEmpty(opcServerInfo)){
            //则说明当前opc已经被移除，无需再进行重连操作
            OpcConstant.OPC_SERVER_ID_CLIENT_MAP.remove(opcServerId);
        }else {
            //获取当前opcServer下的所有节点设备
            List<OpcNodeInfo> opcNodeInfoList = iOpcNodeInfoService.list(
                    new QueryWrapper<OpcNodeInfo>().eq("opc_server_info_id", opcServerId));
            OpcThreadPoolExecutor.initOpcConnectSubNode(opcServerInfo,this,opcNodeInfoList,iOpcNodeInfoService);
        }
    }

    @Override
    public void rebuildOpcSubscribe(String opcServerId, UaClient uaClient) {
        OpcServerInfo opcServerInfo = this.getById(opcServerId);
        if (ObjectUtil.isEmpty(opcServerInfo)){
            try {
                //建立当前客户端监听连接------用于测试uaClient是否可用
                UaSubscription uaSubscription = uaClient.getSubscriptionManager().createSubscription(1000.0).get();
                //开始重新监听所有的节点
                iOpcNodeInfoService.reSubscribeOpcNode(opcServerId);
            } catch (ExecutionException | InterruptedException e) {
                log.error("------------------ opc: {}, 断开连接，尝试重连中-------------------",opcServerInfo.getOpcServerName());
                e.printStackTrace();
                reconnectOpcServer(opcServerId);
            }
        }else {
            //opcServer 当前已不存在------移除
            log.error("===========================    opcId :{} 已不存在    ===========================",opcServerId);
            OpcConstant.OPC_SERVER_ID_CLIENT_MAP.remove(opcServerId);

        }
    }

    /**
     * 断开opc设备连接
     * @param id id
     */
    private void disconnectOpc(String id) {
        UaClient uaClient = OpcConstant.OPC_SERVER_ID_CLIENT_MAP.get(id);
        if (null != uaClient){
            try {
                uaClient.disconnect().get();
            } catch (InterruptedException | ExecutionException e) {
                log.error("opc设备连接断开出现异常: ",e);
            }
            //删除缓存
            OpcConstant.OPC_SERVER_ID_CLIENT_MAP.remove(id);
        }

    }
}
